Skip to content

feat: add Mathpix PDF extraction - #26

Open
m-messer wants to merge 1 commit into
llm-clientfrom
mathpix
Open

m-messer wants to merge 1 commit into
llm-clientfrom
mathpix

Conversation

@m-messer

@m-messer m-messer commented Sep 1, 2026

Copy link
Copy Markdown
Member

in2lambda/wizard/mathpix.py: pdf_to_markdown() uploads a PDF to the Mathpix OCR API, polls for the rendered markdown, downloads any remote figures into <out_dir>/media/, and repoints the markdown at ./media/<name> so the Markdown filter's image resolution finds them.

  • Credentials from $MATHPIX_APP_ID / $MATHPIX_API_KEY; a missing pair raises a clear RuntimeError.
  • Only needs requests (already a core dep), so the module imports without the llm extra.
  • Ported and cleaned up from conversion2025/converter.py on Summer2025: print/exit calls become exceptions, the PIL round-trip is dropped (bytes stream straight to disk), poll interval/count are parameters.
  • Tests mock all HTTP.

⚠️ Not yet exercised against the live Mathpix service — tests are fully mocked. Live PDF run pending.


Stack: llm-client ← mathpix ← wizard-command
Base: llm-client#25.

🤖 Generated with Claude Code

in2lambda/wizard/mathpix.py: pdf_to_markdown() uploads a PDF to the Mathpix
OCR API, polls for the rendered markdown, downloads any remote figures into
<out_dir>/media/, and repoints the markdown at ./media/<name> so the
Markdown filter's image resolution finds them.

- Credentials from $MATHPIX_APP_ID / $MATHPIX_API_KEY; a missing pair raises
  a clear RuntimeError.
- Only needs `requests` (already a core dep), so the module imports without
  the llm extra.
- Ported and cleaned up from conversion2025/converter.py on Summer2025:
  print/exit calls become exceptions, the PIL round-trip is dropped (bytes
  are streamed straight to disk), poll interval/count are parameters.

Tests mock all HTTP.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017VXb8aZqgFBjoeuuddjW6r
@peterbjohnson

Copy link
Copy Markdown
Member

Notes from review. The shape of this is fine; it's the failure paths that need work before it goes near a real PDF.

No timeouts. None of the three HTTP calls (mathpix.py:60, :80, :95) pass timeout=, so a stalled connection hangs the command indefinitely with no way to tell what's happening.

Polling treats every failure as "not ready yet" (:79-84). Any non-200 response, whether that's a bad API key, an unknown id, a server error or a conversion that failed outright, just sleeps and retries. The user waits the full five minutes and is then told the conversion "did not finish in time", which sends them looking in the wrong place. Polling GET /v3/pdf/{id} for the conversion status and raising on an error status would report the real problem in seconds.

response.json()["pdf_id"] (:64) raises a bare KeyError when Mathpix returns an error body, which reads like a bug in in2lambda rather than a rejected upload.

It can overwrite a file the user cares about. pdf_to_markdown writes <pdf stem>.md into out_dir (:68-70), and in the wizard PR out_dir is the folder of the user's chosen output file. So in2lambda wizard notes/paper.pdf -o notes/draft.md silently replaces notes/paper.md if it exists. Returning the markdown as a string, and only writing the figures, would avoid it entirely.

Failed figure downloads are skipped silently (:95-97), leaving the markdown pointing at a local file that was never written. A warning would do.

Two more, both worth a line in the docs rather than code: the PDF is uploaded to a third party, which teachers should be told about, and Mathpix has a training opt-out that we probably want set. Also, has this been run against the real service yet? The tests mock it, so what they check is our assumptions about the API rather than the API itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants